home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / CPP / WFC010.ZIP / SRC / CSERVICE.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-07  |  15.6 KB  |  561 lines

  1. #include <wfc.h>
  2. #pragma hdrstop
  3.  
  4. /*
  5. ** Author: Samuel R. Blackburn
  6. ** CI$: 76300,326
  7. ** Internet: sammy@sed.csc.com
  8. **
  9. ** You can use it any way you like as long as you don't try to sell it.
  10. **
  11. ** Any attempt to sell WFC in source code form must have the permission
  12. ** of the original author. You can produce commercial executables with
  13. ** WFC but you can't sell WFC.
  14. **
  15. ** Copyright, 1995, Samuel R. Blackburn
  16. **
  17. ** $Workfile: $
  18. ** $Revision: $
  19. ** $Modtime: $
  20. */
  21.  
  22. #if defined( _DEBUG )
  23. #undef THIS_FILE
  24. static char BASED_CODE THIS_FILE[] = __FILE__;
  25. #define new DEBUG_NEW
  26. #endif
  27.  
  28. CRITICAL_SECTION g_ServiceCriticalSection;
  29.  
  30. CService *CService::m_Service_p = 0;
  31.  
  32. CService::CService( LPTHREAD_START_ROUTINE thread_start_routine, DWORD controls_accepted, DWORD wait_hint )
  33. {
  34.    ASSERT( thread_start_routine != NULL );
  35.  
  36.    TRACE1( "Main tid = %#lx\n", ::GetCurrentThreadId() );
  37.  
  38.    ::InitializeCriticalSection( &g_ServiceCriticalSection );
  39.  
  40.    m_ThreadStartRoutine  = thread_start_routine;
  41.    m_ThreadHandle        = NULL;
  42.    m_ExitEventHandle     = NULL;
  43.    m_ServiceStatusHandle = 0;
  44.    m_ErrorCode           = NO_ERROR;
  45.    m_Running             = FALSE;
  46.    m_Paused              = FALSE;
  47.    m_Exiting             = FALSE;
  48.    m_Debugging           = 0;
  49.    m_ControlsAccepted    = controls_accepted;
  50.    m_WaitHint            = wait_hint;
  51.    m_CurrentState        = SERVICE_START_PENDING;
  52.    m_Service_p           = this;
  53. }
  54.  
  55. CService::~CService( void )
  56. {
  57.    ::DeleteCriticalSection( &g_ServiceCriticalSection );
  58.  
  59.    if ( m_ExitEventHandle != NULL )
  60.    {
  61.       ::CloseHandle( m_ExitEventHandle );
  62.       m_ExitEventHandle = NULL;
  63.    }
  64.  
  65.    if ( m_ThreadHandle != NULL )
  66.    {
  67.       ::CloseHandle( m_ThreadHandle );
  68.       m_ThreadHandle = NULL;
  69.    }
  70. }
  71.  
  72. BOOL CService::Initialize( LPCTSTR name_of_service )
  73. {
  74.    /*
  75.    ** Thank you Rob Williams (CI$ 73740,774) for fixing this function
  76.    */
  77.  
  78.    ASSERT( name_of_service != NULL );
  79.  
  80.    BOOL return_value = TRUE;
  81.  
  82.    // initialize m_ServiceTable
  83.  
  84.    ::strncpy( m_ServiceName, name_of_service, SERVICE_NAME_LEN );
  85.  
  86.    m_ServiceTable[ 0 ].lpServiceName = m_ServiceName;
  87.    m_ServiceTable[ 0 ].lpServiceProc = CService::ServiceMain;
  88.    m_ServiceTable[ 1 ].lpServiceName = 0;
  89.    m_ServiceTable[ 1 ].lpServiceProc = 0;
  90.  
  91.    // initiate conversation with SCM
  92.  
  93.    if ( ::StartServiceCtrlDispatcher( m_ServiceTable ) == FALSE )
  94.    {
  95.       m_ErrorCode = ::GetLastError();
  96.       return_value = FALSE;
  97.       LogEvent();
  98.    }
  99.  
  100.    return( return_value );
  101. }
  102.  
  103. void CService::AssertValid( void ) const
  104. {
  105.    ASSERT( m_Exiting             != TRUE );
  106.    ASSERT( m_ExitEventHandle     != NULL );
  107.    ASSERT( m_ServiceStatusHandle != 0    );
  108.    ASSERT( m_ThreadHandle        != NULL );
  109.    ASSERT( m_Service_p           != 0    );
  110. }
  111.  
  112. void CALLBACK CService::ServiceMain( DWORD argc, LPTSTR *argv )
  113. {
  114.    // entry point for service called by SCM when service is started
  115.  
  116.    HANDLE thread_handle = NULL;
  117.  
  118.    ASSERT( m_Service_p != NULL );
  119.  
  120.    TRACE1( "ServiceMain tid = %#lx\n", ::GetCurrentThreadId() );
  121.  
  122.    ::EnterCriticalSection( &g_ServiceCriticalSection );
  123.    m_Service_p->m_ServiceStatusHandle = ::RegisterServiceCtrlHandler( TEXT( m_Service_p->m_ServiceName ), ServiceControlManagerHandler );
  124.    ::LeaveCriticalSection( &g_ServiceCriticalSection );
  125.  
  126.    if ( m_Service_p->m_ServiceStatusHandle == (SERVICE_STATUS_HANDLE) 0 )
  127.    {
  128.       m_Service_p->m_ErrorCode = ::GetLastError();
  129.       m_Service_p->LogEvent();
  130.       m_Service_p->Exit();
  131.    }
  132.    else
  133.    {
  134.       if ( ! m_Service_p->SendStatusToServiceControlManager( SERVICE_START_PENDING, NO_ERROR, 1, m_Service_p->m_WaitHint ) )
  135.       {
  136.          goto EXIT_GOTO;
  137.       }
  138.  
  139.       ::EnterCriticalSection( &g_ServiceCriticalSection );
  140.       m_Service_p->m_ExitEventHandle = ::CreateEvent( 0, TRUE, FALSE, 0 );
  141.       ::LeaveCriticalSection( &g_ServiceCriticalSection );
  142.  
  143.       if ( m_Service_p->m_ExitEventHandle == NULL )
  144.       {
  145.          m_Service_p->m_ErrorCode = ::GetLastError();
  146.          m_Service_p->LogEvent();
  147.          m_Service_p->Exit();
  148.       }
  149.       else
  150.       {
  151.          if ( ! m_Service_p->SendStatusToServiceControlManager( SERVICE_START_PENDING, NO_ERROR, 2, m_Service_p->m_WaitHint ) )
  152.          {
  153.             goto EXIT_GOTO;
  154.          }
  155.  
  156.          m_Service_p->ParseCommandLineParameters( argc, argv );
  157.  
  158.          if ( ! m_Service_p->SendStatusToServiceControlManager( SERVICE_START_PENDING, NO_ERROR, 3, m_Service_p->m_WaitHint ) )
  159.          {
  160.             goto EXIT_GOTO;
  161.          }
  162.  
  163.          m_Service_p->OnPrepareServiceThread();
  164.  
  165.          thread_handle = ::CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)
  166.                                                m_Service_p->m_ThreadStartRoutine,
  167.                                                m_Service_p, 0,
  168.                                                &m_Service_p->m_ThreadId );
  169.  
  170.          ::EnterCriticalSection( &g_ServiceCriticalSection );
  171.          m_Service_p->m_ThreadHandle = thread_handle;
  172.          ::LeaveCriticalSection( &g_ServiceCriticalSection );
  173.  
  174.          if ( m_Service_p->m_ThreadHandle == NULL )
  175.          {
  176.             m_Service_p->m_ErrorCode = ::GetLastError();
  177.             m_Service_p->LogEvent();
  178.             m_Service_p->Exit();
  179.          }
  180.          else
  181.          {
  182.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  183.             m_Service_p->m_Running = TRUE;
  184.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  185.  
  186.             if ( m_Service_p->SendStatusToServiceControlManager( SERVICE_RUNNING ) == FALSE )
  187.             {
  188.                return;
  189.             }
  190.  
  191.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  192.             m_Service_p->m_CurrentState = SERVICE_RUNNING;
  193.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  194.  
  195.             ::WaitForSingleObject( m_Service_p->m_ExitEventHandle, INFINITE );
  196.          }
  197.  
  198. EXIT_GOTO:
  199.  
  200.          // notify SCM that service has stopped
  201.  
  202.          ASSERT( m_Service_p != 0 );
  203.  
  204.          if ( m_Service_p->m_ServiceStatusHandle != 0 )
  205.          {
  206.             m_Service_p->SendStatusToServiceControlManager( SERVICE_STOPPED, m_Service_p->m_ErrorCode );
  207.          }
  208.       }
  209.    }
  210. }
  211.  
  212. void CALLBACK CService::ServiceControlManagerHandler( DWORD control_code )
  213. {
  214.    // entry point for service called by SCM after service is started
  215.  
  216.    ASSERT( m_Service_p != 0 );
  217.  
  218.    switch( control_code )
  219.    {
  220.       case SERVICE_CONTROL_STOP:
  221.  
  222.          TRACE1( "Handling SERVICE_CONTROL_STOP tid %#lx\n", ::GetCurrentThreadId() );
  223.  
  224.          m_Service_p->SendStatusToServiceControlManager( SERVICE_STOP_PENDING, NO_ERROR, 1, m_Service_p->m_WaitHint );
  225.  
  226.          ::EnterCriticalSection( &g_ServiceCriticalSection );
  227.          m_Service_p->m_Running = FALSE;
  228.          m_Service_p->m_CurrentState = SERVICE_STOPPED;
  229.          ::LeaveCriticalSection( &g_ServiceCriticalSection );
  230.  
  231.          m_Service_p->OnStop();
  232.          m_Service_p->Exit();
  233.  
  234.          return;
  235.  
  236.       case SERVICE_CONTROL_PAUSE:
  237.  
  238.          TRACE1( "Handling SERVICE_CONTROL_PAUSE tid %#lx\n", ::GetCurrentThreadId() );
  239.  
  240.          if ( m_Service_p->m_Running == TRUE && m_Service_p->m_Paused != TRUE )
  241.          {
  242.             if ( m_Service_p->SendStatusToServiceControlManager( SERVICE_PAUSE_PENDING, NO_ERROR, 1, m_Service_p->m_WaitHint ) == FALSE )
  243.             {
  244.                return;
  245.             }
  246.  
  247.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  248.             m_Service_p->m_Paused = TRUE;
  249.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  250.  
  251.             m_Service_p->OnPause();
  252.             ::SuspendThread( m_Service_p->m_ThreadHandle );
  253.  
  254.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  255.             m_Service_p->m_CurrentState = SERVICE_PAUSED;
  256.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  257.          }
  258.  
  259.          break;
  260.  
  261.       case SERVICE_CONTROL_CONTINUE:
  262.  
  263.          TRACE1( "Handling SERVICE_CONTROL_CONTINUE tid %#lx\n", ::GetCurrentThreadId() );
  264.  
  265.          if ( m_Service_p->m_Running == TRUE && m_Service_p->m_Paused == TRUE )
  266.          {
  267.             if ( m_Service_p->SendStatusToServiceControlManager( SERVICE_CONTINUE_PENDING, NO_ERROR, 1, m_Service_p->m_WaitHint ) == FALSE )
  268.             {
  269.                return;
  270.             }
  271.  
  272.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  273.             m_Service_p->m_Paused = FALSE;
  274.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  275.  
  276.             ::ResumeThread( m_Service_p->m_ThreadHandle );
  277.             m_Service_p->OnContinue();
  278.  
  279.             ::EnterCriticalSection( &g_ServiceCriticalSection );
  280.             m_Service_p->m_CurrentState = SERVICE_RUNNING;
  281.             ::LeaveCriticalSection( &g_ServiceCriticalSection );
  282.          }
  283.  
  284.          break;
  285.  
  286.       case SERVICE_CONTROL_INTERROGATE:
  287.  
  288.          TRACE1( "Handling SERVICE_CONTROL_INTERROGATE tid %#lx\n", ::GetCurrentThreadId() );
  289.          break;
  290.  
  291.       case SERVICE_CONTROL_SHUTDOWN:
  292.  
  293.          TRACE1( "Handling SERVICE_CONTROL_SHUTDOWN tid %#lx\n", ::GetCurrentThreadId() );
  294.          break;
  295.  
  296.       default:
  297.  
  298.          TRACE3( "Handling user-defined control code %#ld (%ld) tid %#lx\n", control_code, control_code, ::GetCurrentThreadId() );
  299.          m_Service_p->OnControlCode( control_code );
  300.  
  301.          break;
  302.    }
  303.  
  304.    m_Service_p->SendStatusToServiceControlManager( m_Service_p->m_CurrentState );
  305. }
  306.  
  307. void CService::ParseCommandLineParameters( DWORD argc , LPTSTR *argv )
  308. {
  309.    DWORD argument_number = 1;
  310.  
  311.    // default implementation
  312.    // parse command line parameters passed via SCM through ServiceMain
  313.  
  314.    while( argument_number < argc )
  315.    {
  316.       if ( argv[ argument_number ][ 0 ] == '-' || argv[ argument_number][ 0 ] == '/' )
  317.       {
  318.          switch( argv[ argument_number ][ 1 ] )
  319.          {
  320.             case 'd':
  321.             case 'D':
  322.  
  323.                ::EnterCriticalSection( &g_ServiceCriticalSection );
  324.                m_Debugging = 1;
  325.                ::LeaveCriticalSection( &g_ServiceCriticalSection );
  326.  
  327.                break;
  328.  
  329.             case 'i':
  330.             case 'I':
  331.  
  332.                char message_string[ 80 ];
  333.  
  334.                ::sprintf( message_string, "pid %#lx %ld", ::GetCurrentProcessId(), ::GetCurrentProcessId() );
  335.                ::MessageBox( NULL, message_string, m_ServiceName, MB_OK );
  336.  
  337.                break;
  338.  
  339.             default:
  340.  
  341.                break;
  342.         }
  343.       }
  344.  
  345.       argument_number++;
  346.    }
  347.  
  348. }
  349.  
  350. void CService::OnControlCode( DWORD /* dwControlCode */ )
  351. {
  352.    // default implementation
  353.    // handle user-defined control codes (128 - 255 inclusive)
  354. }
  355.  
  356. void CService::OnStop( void )
  357. {
  358.    // default implementation
  359. }
  360.  
  361. void CService::OnPrepareServiceThread( void )
  362. {
  363.    // default implementation
  364.    // allows for initialization prior to creating service thread
  365. }
  366.  
  367. void CService::OnPause( void )
  368. {
  369.    CEventLog log( m_ServiceName );
  370.    log.ReportInformation( "Service Paused" );
  371. }
  372.  
  373. void CService::OnContinue( void )
  374. {
  375.    CEventLog log( m_ServiceName );
  376.    log.ReportInformation( "Service Resumed" );
  377. }
  378.  
  379. BOOL CService::SendStatusToServiceControlManager( DWORD current_state, 
  380.                                                   DWORD win32_exit_code,
  381.                                                   DWORD check_point,
  382.                                                   DWORD wait_hint,
  383.                                                   DWORD service_specific_code )
  384. {
  385.    BOOL return_value = FALSE;
  386.  
  387.    SERVICE_STATUS service_status;
  388.  
  389.    ::ZeroMemory( &service_status, sizeof( service_status ) );
  390.  
  391.    // initialize service_status and send it to SCM
  392.  
  393.    if ( current_state == SERVICE_START_PENDING )
  394.    {
  395.       service_status.dwControlsAccepted = 0;
  396.    }
  397.    else
  398.    {
  399.       service_status.dwControlsAccepted = m_ControlsAccepted;
  400.    }
  401.  
  402.    if ( service_specific_code == 0 )
  403.    {
  404.       service_status.dwWin32ExitCode = win32_exit_code;
  405.    }
  406.    else
  407.    {
  408.       service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  409.    }
  410.  
  411.    service_status.dwServiceType             = SERVICE_WIN32_OWN_PROCESS;
  412.    service_status.dwCurrentState            = current_state;
  413.    service_status.dwServiceSpecificExitCode = service_specific_code;
  414.    service_status.dwCheckPoint              = check_point;
  415.    service_status.dwWaitHint                = wait_hint;
  416.  
  417. #if defined( _DEBUG )
  418.    DumpStatus( &service_status );
  419. #endif
  420.  
  421.    return_value = ::SetServiceStatus( m_ServiceStatusHandle, &service_status );
  422.  
  423.    if ( return_value == FALSE )
  424.    {
  425.       m_ErrorCode = ::GetLastError();
  426.       LogEvent();
  427.       Exit();
  428.    }
  429.  
  430.    return( return_value );
  431. }
  432.  
  433. void CService::Exit( void )
  434. {
  435.    ASSERT_VALID( this );
  436.  
  437.    ::EnterCriticalSection( &g_ServiceCriticalSection );
  438.  
  439.    m_Running      = FALSE;
  440.    m_CurrentState = SERVICE_STOPPED;
  441.    m_Exiting      = TRUE;
  442.  
  443.    ::LeaveCriticalSection( &g_ServiceCriticalSection );
  444.  
  445.    if ( m_ExitEventHandle != NULL )
  446.    {
  447.       ::SetEvent( m_ExitEventHandle );
  448.    }
  449. }
  450.  
  451. #pragma warning( disable : 4100 )
  452.  
  453. void CService::LogEvent( WORD event_type, LPTSTR message_string, DWORD error_code )
  454. {
  455.    CEventLog log( m_ServiceName );
  456.  
  457.    LPTSTR strings[ 1 ];
  458.  
  459.    strings[ 0 ] = message_string;
  460.  
  461.    log.Report( (CEventLog::EventType) event_type, 0, 0, 1, (const char **) strings );
  462. }
  463.  
  464. #pragma warning( default : 4100 )
  465.  
  466. #if defined( _DEBUG )
  467.  
  468. void CService::DumpStatus( SERVICE_STATUS *pStatus ) const
  469. {
  470.    TRACE( "\ncalling SetServiceStatus with:\n" );
  471.  
  472.    switch( pStatus->dwServiceType )
  473.    {
  474.       case SERVICE_WIN32_OWN_PROCESS:
  475.  
  476.          TRACE( "dwServiceType SERVICE_WIN32_OWN_PROCESS\n" );
  477.          break;
  478.  
  479.       case SERVICE_WIN32_SHARE_PROCESS:
  480.  
  481.          TRACE( "dwServiceType SERVICE_WIN32_SHARE_PROCESS\n" );
  482.          break;
  483.    }
  484.  
  485.    TRACE1( "dwControlsAccepted %#lx:\n", pStatus->dwControlsAccepted );
  486.    TRACE( "   SERVICE_CONTROL_INTERROGATE\n" );
  487.  
  488.    if ( pStatus->dwControlsAccepted & SERVICE_ACCEPT_STOP )
  489.    {
  490.       TRACE( "   SERVICE_CONTROL_STOP\n" );
  491.    }
  492.  
  493.    if ( pStatus->dwControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE )
  494.    {
  495.       TRACE( "   SERVICE_CONTROL_PAUSE\n" );
  496.       TRACE( "   SERVICE_CONTROL_CONTINUE\n" );
  497.    }
  498.  
  499.    if ( pStatus->dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN )
  500.    {
  501.       TRACE( "   SERVICE_CONTROL_SHUTDOWN\n" );
  502.    }
  503.  
  504.    switch( pStatus->dwCurrentState )
  505.    {
  506.       case SERVICE_STOPPED:
  507.  
  508.          TRACE( "dwCurrentState SERVICE_STOPPED\n" );
  509.          break;
  510.  
  511.       case SERVICE_START_PENDING:
  512.  
  513.          TRACE( "dwCurrentState SERVICE_START_PENDING\n" );
  514.          break;
  515.  
  516.       case SERVICE_STOP_PENDING:
  517.  
  518.          TRACE( "dwCurrentState SERVICE_STOP_PENDING\n" );
  519.          break;
  520.  
  521.       case SERVICE_RUNNING:
  522.  
  523.          TRACE( "dwCurrentState SERVICE_RUNNING\n" );
  524.          break;
  525.  
  526.       case SERVICE_CONTINUE_PENDING:
  527.  
  528.          TRACE( "dwCurrentState SERVICE_CONTINUE_PENDING\n" );
  529.          break;
  530.  
  531.       case SERVICE_PAUSE_PENDING:
  532.  
  533.          TRACE( "dwCurrentState SERVICE_PAUSE_PENDING\n" );
  534.          break;
  535.  
  536.       case SERVICE_PAUSED:
  537.  
  538.          TRACE( "dwCurrentState SERVICE_PAUSED\n" );
  539.          break;
  540.  
  541.       default:
  542.  
  543.          TRACE2( "dwCurrentState %#lx (%ld)\n", pStatus->dwCurrentState, pStatus->dwCurrentState );
  544.          break;
  545.    }
  546.  
  547.    if ( pStatus->dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR )
  548.    {
  549.       TRACE2( "dwServiceSpecificExitCode %#lx (%ld)\n", pStatus->dwServiceSpecificExitCode, pStatus->dwServiceSpecificExitCode );
  550.    }
  551.    else
  552.    {
  553.       TRACE2( "dwWin32ExitCode %#lx (%ld)\n", pStatus->dwWin32ExitCode, pStatus->dwWin32ExitCode );
  554.    }
  555.  
  556.    TRACE1( "dwCheckPoint %ld\n", pStatus->dwCheckPoint );
  557.    TRACE1( "dwWaitHint %ld\n\n", pStatus->dwWaitHint );
  558. }
  559.  
  560. #endif // _DEBUG
  561.